/*
 * Copyright (C) 2008 Rob Cash
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.robcash.commons.swing.wizard.impl;

import java.awt.Component;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.robcash.commons.swing.wizard.Wizard;
import org.robcash.commons.swing.wizard.WizardPanel;

/**
 * Base class for wizard panel implementations. It is recommended that
 * applications override this class with their own concrete implementation.
 * Specifically, subclasses should consider the following:
 * <ul>
 * <li>Override {@link #getPreviousPanelId()} so that it returns something
 * other than <tt>null</tt>. This can be a static or dynamic value.</li>
 * <li>Override {@link #getNextPanelId()} so that it returns something
 * other than <tt>null</tt>. This can be a static or dynamic value.</li>
 * <li>If the previous and/or next panel IDs are determined dynamically,
 * consider overriding {@link #setComponent(java.awt.Component)} so that a
 * listener can be set up. This will enable the component to notify the wizard
 * panel when it has changed or been updated, which may indicate that the
 * previous and/or next panel IDs have changed.</li>
 * </ul>
 * 
 * @author Rob
 * @param <T> Type of component contained by the wizard panel
 */
public class BaseWizardPanel<T extends Component> implements WizardPanel<T> {

    /**
     * Unique identifier
     */
    private String id;

    /**
     * Component
     */
    protected T component;

    /**
     * Wizard
     */
    protected Wizard wizard;
    
    /**
     * Listener list
     */
    protected final EventListenerList listeners;

    /**
     * Create a new wizard panel
     * @param id Unique identifier
     */
    public BaseWizardPanel(final String id) {
        this.id = id;
        this.listeners = new EventListenerList();
    }

    /**
     * Create a new wizard panel
     * @param id Unique identifier
     * @param component Component to display when active
     */
    public BaseWizardPanel(final String id, T component) {
        this(id);
        setComponent(component);
    }

    /**
     * Get the unique identifier for this wizard panel
     * @return unique identifier
     */
    final public String getId() {
        return id;
    }

    /**
     * Indicates if this panel allows backwards traversal. This implementation
     * always allows the user to go backwards.
     * @return true if this panel allows the user to go "back"
     */
    public boolean canGoBack() {
        return true;
    }

    /**
     * Indicates if this panel allows forwards traversal
     * @return true if this panel allows the user to go "forward"
     */
    public boolean canGoForward() {
        return getNextPanelId() != null;
    }

    /**
     * Indicates if this panel allows the wizard to complete
     * @return true if this panel allows the user to "finish"
     */
    public boolean canFinish() {
        return false;
    }

    /**
     * Get the next wizard panel's identifier
     * @return Wizard panel identifier. This implementation always returns
     * <tt>null</tt>, which indicates that this panel does not have a next
     * panel.
     */
    public String getNextPanelId() {
        return null;
    }

    /**
     * Get the visual component wrapped by this wizard panel
     * @return Component
     */
    public T getComponent() {
        return component;
    }

    /**
     * Set the visual component wrapped by this wizard panel
     * @param component Component to set
     */
    public void setComponent(T component) {
        this.component = component;
    }

    /**
     * Indicates that the wizard panel is about to be displayed. This
     * implementation only sets the wizard from the event.
     * @param wizard Wizard that is about to display this panel
     */
    public void aboutToDisplay(Wizard wizard) {
        this.wizard = wizard;
    }

    /**
     * Indicates that the wizard panel is about to be displayed. This
     * implementation clears the wizard.
     * @param wizard Wizard that is about to hide this panel
     * @param saveWork Indicates whether or not work should be saved
     */
    public void aboutToHide(Wizard wizard, boolean saveWork) {
        this.wizard = null;
    }

    /**
     * Indicates that the wizard panel has just been displayed. This
     * implementation does nothing in response to the event.
     * @param wizard Wizard that has just displayed this panel
     */
    public void displayed(Wizard wizard) {
    }

    /**
     * Add a change listener
     * @param listener Listener to add
     */
    public void addChangeListener(ChangeListener listener) {
        synchronized(listeners) {
            listeners.add(ChangeListener.class, listener);
        }
    }

    /**
     * Remove a change listener
     * @param listener Listener to remove
     */
    public void removeChangeListener(ChangeListener listener) {
        synchronized(listeners) {
            listeners.remove(ChangeListener.class, listener);
        }
    }

    /**
     * Notify listeners that a change has taken place
     * @param evt Change event
     */
    protected void fireChangeListenerStateChanged(ChangeEvent evt) {
        ChangeListener[] changeListeners;
        synchronized(listeners) {
            changeListeners = listeners.getListeners(ChangeListener.class);
        }
        for( int index = 0; index < changeListeners.length; index++ ) {
            changeListeners[index].stateChanged(evt);
        }
    }

}
